package online.heycm.platform.web.common.filter;

import cn.hutool.core.lang.UUID;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import online.heycm.platform.common.entity.constant.Constant;
import online.heycm.platform.common.entity.result.Result;
import online.heycm.platform.common.tools.Jackson;
import online.heycm.platform.web.common.filter.wrapper.CacheRequestWrapper;
import online.heycm.platform.web.common.filter.wrapper.CacheResponseWrapper;
import org.slf4j.MDC;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

/**
 * 链路日志
 *
 * @author heycm
 * @since 2023/12/9 17:22
 */
@Slf4j
public class TraceFilter extends OncePerRequestFilter implements Ordered {

    private final int order;

    public TraceFilter(int order) {
        this.order = order;
    }

    @Override
    public int getOrder() {
        return order;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        CacheRequestWrapper requestWrapper = null;
        CacheResponseWrapper responseWrapper = null;

        setTraceId(request);

        String uri = request.getRequestURI();
        String parameter;
        long l = System.currentTimeMillis();
        if (skipContentType(request)) {
            parameter = "接口暂不支持解析参数";
        } else {
            requestWrapper = new CacheRequestWrapper(request);
            parameter = RequestHelper.getParameter(requestWrapper);
        }
        log.info("请求 {} 参数: {}", uri, parameter);

        if (!skipContentType(request, response)) {
            responseWrapper = new CacheResponseWrapper(response);
        }

        chain.doFilter(requestWrapper == null ? request : requestWrapper, responseWrapper == null ? response : responseWrapper);

        if (responseWrapper != null) {
            String contentType = responseWrapper.getHeader(HttpHeaders.CONTENT_TYPE);
            if (contentType != null && contentType.contains(MediaType.APPLICATION_JSON_VALUE)) {
                Result result = Jackson.toObject(responseWrapper.getContentAsByteArray(), Result.class);
                if (result != null) {
                    log.info("请求 {} 响应 {} 耗时: {}ms", uri, result.toJsonWithoutData(),
                            (System.currentTimeMillis() - l));
                    responseWrapper.writeToClient();
                    return;
                }
            }
            log.info("请求 {} 耗时: {}ms", uri, (System.currentTimeMillis() - l));
            responseWrapper.writeToClient();
        } else {
            log.info("请求 {} 耗时: {}ms", uri, (System.currentTimeMillis() - l));
        }
    }

    private boolean skipContentType(HttpServletRequest request) {
        String contentType = request.getContentType();
        String accept = request.getHeader(HttpHeaders.ACCEPT);
        if (StringUtils.hasText(contentType) && contentType.contains(MediaType.MULTIPART_FORM_DATA_VALUE)) {
            return true;
        }
        if (StringUtils.hasText(contentType) && contentType.contains(MediaType.TEXT_EVENT_STREAM_VALUE)) {
            return true;
        }
        if (StringUtils.hasText(accept) && accept.contains(MediaType.TEXT_EVENT_STREAM_VALUE)) {
            return true;
        }
        return false;
    }

    private boolean skipContentType(HttpServletRequest request, HttpServletResponse response) {
        String contentType = response.getContentType();
        String accept = response.getHeader(HttpHeaders.ACCEPT);
        String requestAccept = request.getHeader(HttpHeaders.ACCEPT);
        if (StringUtils.hasText(contentType) && contentType.contains(MediaType.APPLICATION_OCTET_STREAM_VALUE)) {
            return true;
        }
        if (StringUtils.hasText(contentType) && contentType.contains(MediaType.TEXT_EVENT_STREAM_VALUE)) {
            return true;
        }
        if (StringUtils.hasText(accept) && accept.contains(MediaType.TEXT_EVENT_STREAM_VALUE)) {
            return true;
        }
        if (StringUtils.hasText(requestAccept) && requestAccept.contains(MediaType.TEXT_EVENT_STREAM_VALUE)) {
            return true;
        }
        return false;
    }

    private void setTraceId(HttpServletRequest request) {
        String traceId = request.getHeader(Constant.TRACE_ID);
        String stepId = request.getHeader(Constant.STEP_ID);

        if (StringUtils.isEmpty(traceId)) {
            traceId = UUID.fastUUID().toString(true).replaceAll("-", "");
        }
        if (StringUtils.isEmpty(stepId)) {
            stepId = "1";
        } else {
            stepId = (Integer.parseInt(stepId) + 1) + "";
        }

        MDC.put(Constant.TRACE_ID, traceId);
        MDC.put(Constant.STEP_ID, stepId);
    }
}
